#include <iostream>
#include <cstring>
#include <list>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <pthread.h>
#include <ctime>

// 延时
#include <thread>
#include <chrono>

// 设置套接字
#include <fcntl.h>

#define IP "192.168.1.106"

#define SENDPORT 2000
#define RECVPORT 2001

#define HEARTBEAT 0x44 // 心跳包,用于服务器更新用户收发的端口信息
#define SENDTO 0x55    // 握手
#define SENDOUT 0xff   // 通信结束
#define ADDGREP 0x0A   // 加入群聊
#define DATAINF 0x02   // 此次发送的数据信息
#define DATA 0x0f      // 数据包
#define _DATAPAEK 10

#pragma pack(push, 4) // 确保双方字节对其

struct date
{
    char file_type;       // 此包的传输的数据消息类型
    size_t grep_id;       // 目标ID
    size_t sleek;         // 此包数据的指针的位置
    size_t size;          // 数据包中 DATA 的字节数 和自己的账号ID
    char talk[_DATAPAEK]; // 数据 和 自己的名字
};

struct grep_inf
{
};

date us;

std::list<grep_inf> grep;
struct sockaddr_in send_addr, recv_addr; // 固定监听
int send_skd, recv_skd;

void Log_in(size_t grep, date dat_inf);
void *recv_thread(void *argc)
{
    socklen_t recv_len = sizeof(recv_addr);
    if (-1 == recv_skd)
    {
        perror("socket error");
        exit(EXIT_FAILURE);
    }

    ssize_t ret = 0, sleek = 0, Size = 0;

    date dat;

    std::string buf;
    char *buff = new char[2000]();
    struct timeval timeout;

    int ptf = 0;
    while (1)
    {
        ret = recvfrom(recv_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&recv_addr, &recv_len);

        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        // 设置套接字超时
        if (setsockopt(recv_skd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == -1)
        {
            perror("设置套接字超时失败");
        }

        if ((ptf == 0) && (dat.file_type == DATAINF))
        {
            std::cout << "群(" << dat.grep_id << ")"
                      << "用户(" << dat.talk << "ID:" << dat.size << ")" << std::endl;
            ptf = 1;
            continue;
        }
        else if (-1 != ret)
        {
            sleek += _DATAPAEK;
            Size = dat.size;
            buf += dat.talk;

            if (((sleek >= Size) && ptf))
            {
                ptf = 0;
                timeout.tv_sec = 0; // 设置超时时间为0秒，即阻塞模式
                timeout.tv_usec = 0;
                if (setsockopt(recv_skd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == -1)
                {
                    perror("设置套接字回阻塞模式失败");
                }

                std::cout << "说:" << buf << std::endl
                          << "输入" << std::endl;

                buf = "";

                sleek = 0;
                Size = 0;
                continue;
            }
        }
        if (-1 == ret)
        {
            std::cout << "异常退出" << std::endl;

            ptf = 0;
            timeout.tv_sec = 0; // 设置超时时间为0秒，即阻塞模式
            timeout.tv_usec = 0;
            if (setsockopt(recv_skd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == -1)
            {
                perror("设置套接字回阻塞模式失败");
            }

            std::cout << "说:" << buf << std::endl
                      << "输入" << std::endl;

            buf = "";

            sleek = 0;
            Size = 0;
        }
    }
}

void *send_thread(void *argc)
{

    pthread_detach(pthread_self());

    ssize_t ret = 0;

    char *buff = new char[1000]();
    std::string buf = "";
    int rtn = 0;

    while (1)
    {
        date dat;
        memset(buff, '\0', 1000);

        std::cout << "请输入:" << std::endl;
        buf = "";

        std::cin >> buf;

        // fgets(buff, 1000, stdin);

        dat.file_type = SENDTO;
        dat.grep_id = us.grep_id;
        strcpy(dat.talk, us.talk);
        dat.size = us.size;

        // 发送握手包 包含信息
        sendto(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&send_addr, sizeof(send_addr));
        struct sockaddr_in tmp_send_addr = send_addr; // 等待对方返回握手
        socklen_t send_len = sizeof(tmp_send_addr);

        rtn = recvfrom(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&tmp_send_addr, &send_len);
        if (rtn == -1)
        {
            // 等待失败
        }

        dat.file_type = DATA; // 发送数据包，设置类型为DATA

        size_t totalSize = buf.size();
        // size_t totalSize = strlen(buff);

        dat.size = totalSize; // 发送数据大小

        sendto(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&tmp_send_addr, sizeof(tmp_send_addr));

        // 等待握手包
        rtn = recvfrom(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&tmp_send_addr, &send_len);
        if (rtn == -1)
        {
            // 等待失败
        }

        dat.sleek = 0;
        while (dat.sleek < totalSize) // 10 - 0 = 10
        {

            dat.file_type = DATA; // 设置数据模式
            if ((dat.size = (totalSize - dat.sleek)) >= _DATAPAEK)
            {
                dat.size = _DATAPAEK;
            }
            // ((dat.size = (totalSize - dat.sleek)) >= _DATAPAEK) ? dat.size = _DATAPAEK : dat.size; // 设置此次数据大小

            memcpy(dat.talk, buf.c_str() + dat.sleek, dat.size);

            sendto(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&tmp_send_addr, sizeof(tmp_send_addr));
            dat.sleek += dat.size; // 数据指针后移
        }
    }

    pthread_exit(nullptr);
}

int main(int argc, char **argv)
{
    send_skd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == send_skd)
    {
        perror("send_skd error");
        exit(EXIT_FAILURE);
    }

    recv_skd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == recv_skd)
    {
        perror("recv_skd error");
        exit(EXIT_FAILURE);
    }

    ssize_t ret = 0;

    send_addr.sin_family = AF_INET;
    send_addr.sin_addr.s_addr = inet_addr(IP); //
    send_addr.sin_port = htons(SENDPORT);      // 对方接收端口

    recv_addr.sin_family = AF_INET;
    // recv_addr.sin_addr.s_addr = inet_addr("192.168.0.31"); //
    // recv_addr.sin_port = htons(RECVPORT);                  // 对方发送端口

    //----------------测试-----------------\\

    struct date dat = {};
    {

        us.grep_id = atoi(argv[1]); // 群聊的ID
        us.size = atoi(argv[2]);    // 用户ID
        strcpy(us.talk, argv[3]);   // name
        Log_in(1000, us);
    }

    //-----------------
    pthread_t thread, recv_th;

    pthread_create(&thread, nullptr, send_thread, &send_addr);
    pthread_create(&recv_th, nullptr, recv_thread, &send_addr);

    pthread_join(thread, nullptr);
    pthread_join(recv_th, nullptr);

    return 0;

    while (1)
    {
        char buf[1000];
        fgets(buf, 1000, stdin);
        // std::cin >> buf;

        dat.file_type = SENDTO;
        // 发送握手
        sendto(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&send_addr, sizeof(send_addr));

        // printf("已发送请求，正在等待对方动作\n");

        struct sockaddr_in send_inf;
        socklen_t addr_len = sizeof(send_inf);

        // 接收握手信息
        recvfrom(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&send_inf, &addr_len);

        dat.file_type = DATAINF;
        ssize_t Size = strlen(buf);
        dat.size = Size;

        // 发送此次传输的数据信息
        sendto(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)(&send_inf), sizeof(send_inf));

        // 等待对方准备接收
        recvfrom(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)(&send_inf), &addr_len);

        ssize_t dat_sleek = 0;

        int i = 0;
        // 发送数据
        while (dat_sleek < Size)
        {
            dat.file_type = DATA;
            dat.sleek = dat_sleek;
            dat.size = (Size - dat_sleek) >= 400 ? 400 : Size - dat_sleek;
            memcpy(dat.talk, buf, dat.size);

            sendto(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)(&send_inf), sizeof(send_inf));
            dat_sleek += 400;
        }

        dat.file_type = SENDOUT;
        sendto(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&send_inf, sizeof(send_inf));
    }
}

// 加入群聊
void Log_in(size_t grep, date dat_inf)
{
    int rtn = 0;
    struct sockaddr_in tmp_send_addr = send_addr;
    socklen_t send_len = sizeof(tmp_send_addr);

    date dat, tmp_dat;
    dat = dat_inf;
    dat.file_type = ADDGREP; // 进群
    // dat.grep_id = grep;      // 群号
    // dat.size = 1010;

    // std::cout << "ID" << dat.size << "群号" << dat.grep_id << "name" << dat.talk << std::endl;
    // 如果发包失败，尝试重连，多次尝试均失败，表示连接不上服务器
    // for (int i = 0; i < 4; i++)
    // {

    // 先发送给服务器的监听端口，让服务器获取客户端的发送端口
    sendto(send_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&send_addr, sizeof(send_addr));
    std::cout << "本机用户:" << dat.talk << std::endl;

    // 等待服务器响应，
    rtn = recvfrom(send_skd, &tmp_dat, sizeof(tmp_dat), 0, (struct sockaddr *)&tmp_send_addr, &send_len); // 监听端口不能动所以这里使用临时变量接收对方临时端口
    // if (rtn == -1)
    // {
    //     // 表示超时
    //     break;
    // }
    // }

    // if (rtn != -1)
    // {
    //     // 表示多次发包无果，结束通讯
    //     return;
    // }
    // for (int i = 0; i < 4; i++)
    // {
    // 用客户端的接收端口 向服务器发握手包提交接收端口信息
    sendto(recv_skd, &dat, sizeof(dat), 0, (struct sockaddr *)&tmp_send_addr, sizeof(tmp_send_addr)); // 监听端口不能动所以这里使用临时变量接收对方临时端口

    // recv_addr = tmp_send_addr;

    // 用服务器的接收端接收消息 确保成功
    rtn = recvfrom(recv_skd, &tmp_dat, sizeof(tmp_dat), 0, (struct sockaddr *)&tmp_send_addr, &send_len); // 监听端口不能动所以这里使用临时变量接收对方临时端口
    std::cout << "成功握手连接服务器" << std::endl;
    //     if (recv_skd != -1)
    //     {
    //         break;
    //     }
    // }
    // if (recv_skd == -1)
    // {
    //     // 表示多次发包无果，结束通讯
    //     return;
    // }
}